using System;
using System.Drawing;
using System.Collections.Generic;
using Clandestine.Graphics;
using Clandestine.Engines.Field;
using Clandestine.Base;

namespace Shlick
{
	public class TilePainterTool : Tool	
	{
		public MapDocument MapDocument { get; private set; } 
        public ToolManager ToolManager { get { return this.MapDocument.ToolManager; } } 
        public Map Map { get { return this.ToolManager.Map; } } 
        public override string Name { get { return "Tile Painter"; } }
        public override event ToolCompleteHandler ToolComplete; // not used.

		public Spritesheet CurrentSpritesheet { get; set; }
		public Rectangle SpritesheetSelection { get; set; }

        /// <summary>
        /// The sprite used for the previews as we drag the mouse.
        /// </summary>
        private static Sprite hoverSprite;

        /// <summary>
        /// Whether we're erasing or not. (i.e. whether right mouse button was clicked or not)
        /// </summary>
        private bool isSelecting = false;

        /// <summary>
        /// Whether the mouse button is being held down.
        /// </summary>
        private bool down = false;

        /// <summary>
        /// The point at which the mouse was pressed down - to allow dragging.
        /// </summary>
        private Point downPosition = new Point();

        /// <summary>
        /// Current position as we drag the mouse.
        /// </summary>
        private Point currentPosition = new Point();
	
        public TilePainterTool(MapDocument mapDocument)
        {
            this.MapDocument = mapDocument;
            hoverSprite = new Sprite(this.MapDocument.HudRenderList, "random/white.png");
            hoverSprite.Visible = false;
            hoverSprite.Color = new Color4(0f, 1f, 1f, 0.6f);
        }

        ~TilePainterTool()
        {
            hoverSprite.Dispose();
        }

        public override void Activated(params object[] args)
        {
        }

        public override void Deactivated()
        {
            if (this.ToolManager.ActiveLayer == null)
        		return;
        	
			if (hoverSprite.Visible)
				hoverSprite.Visible = false;
        	this.MapDocument.ToolManager.ActiveLayer.ClearTemporaryTiles();
        }
        
        public override void LayerChanged (Layer oldLayer)
		{
			if (oldLayer != null)
				oldLayer.ClearTemporaryTiles();
		}

        public override void MouseUpLeft(int mouseX, int mouseY, int tileX, int tileY)
        {
        	if (this.ToolManager.ActiveLayer == null)
        		return;
        
        	if (down == false)
        		return; // oops.
        
            // No longer down.
            down = false;

            // Check they're valid!
            if (downPosition.Y == -1 || downPosition.Y == -1)
                return;

            // Paint the tiles, false to show not temp!
            doPaintTileOp(getTileRect(downPosition.X, downPosition.Y, tileX, tileY, true), false);

            downPosition = currentPosition = new Point(-1, -1);
        }

        public override void MouseDownLeft(int mouseX, int mouseY, int tileX, int tileY)
        {
       		if (this.ToolManager.ActiveLayer == null)
        		return;
        		
            // If we're not already erasing
            if (!(isSelecting && down))
            {
                // remember where we went down and that we went down
                downPosition = currentPosition = new Point(tileX, tileY);
                down = true;
            }
            else
            {
                // if we were erasing, just stop erasing.
                down = isSelecting = false;
                downPosition = currentPosition = new Point(-1, -1);
                this.ToolManager.ActiveLayer.ClearTemporaryTiles();
                hoverSprite.Visible = false;
            }
        }

        public override void MouseUpRight(int mouseX, int mouseY, int tileX, int tileY)
        {
       		if (this.ToolManager.ActiveLayer == null)
        		return;
        
       	    // Are we erasing?
            if (isSelecting && down)
            {
                // get co-ords of top left of the selection - note, we can move the mouse behind the original point
                // so must handle this!
                Point topLeft = new Point();
                topLeft.X = (currentPosition.X < downPosition.X ? currentPosition.X : downPosition.X);
                topLeft.Y = (currentPosition.Y < downPosition.Y ? currentPosition.Y : downPosition.Y);

                // get co-ords of the bottom right
                Point bottomRight = new Point();
                bottomRight.X = (currentPosition.X > downPosition.X ? currentPosition.X : downPosition.X) + 1;
                bottomRight.Y = (currentPosition.Y > downPosition.Y ? currentPosition.Y : downPosition.Y) + 1;

				
				// Select & pop a context menu!
				Rectangle r = new Rectangle(topLeft, new Size(bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y));
				TilesMenu tm = new TilesMenu(this.MapDocument, this.ToolManager.ActiveLayer, r);
				tm.Show();
				
                // Perform the erase
                //this.MapDocument.CommandManager.Do(PaintTilesCommand.EraseTiles(this.ToolManager.ActiveLayer, new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y)));

                // Stop erasing/hover mode
                isSelecting = down = hoverSprite.Visible = false;
                downPosition = currentPosition = new Point(-1, -1);
            }
        }


        public override void MouseDownRight(int mouseX, int mouseY, int tileX, int tileY)
        {
       		if (this.ToolManager.ActiveLayer == null)
        		return;
        
            // Right-click.
            if (!down)
            {
                isSelecting = down = true;
                currentPosition = downPosition = new Point(tileX, tileY);
                hoverSprite.Visible = true;
                hoverSprite.Depth = 1000; //lolz
                hoverSprite.Transform.X = downPosition.X * this.Map.GridSize;
              	hoverSprite.Transform.Y = downPosition.Y * this.Map.GridSize;
                hoverSprite.Transform.ScaleX = (tileX + 1 - downPosition.X) * this.Map.GridSize;
                hoverSprite.Transform.ScaleY = (tileY + 1 - downPosition.Y) * this.Map.GridSize;
            }
            else
            {
                down = false;
                downPosition = currentPosition = new Point(-1, -1);
            }

            this.ToolManager.ActiveLayer.ClearTemporaryTiles();
        }
		
		private Rectangle getTileRect(int ox, int oy, int cx, int cy, bool clever)
		{
			Rectangle r = new Rectangle();
			
			// If the user drags upwards, we should only budge up or left in increments of spritesheetselection's size.
			if (clever && !SpritesheetSelection.IsEmpty)
			{
				if (cx < ox)
					cx += Math.Abs((ox - cx) % SpritesheetSelection.Width);
				if (cy < oy)
					cy += Math.Abs((oy - cy) % SpritesheetSelection.Height);
			}
			
			r.X = (cx < ox ? cx : ox);
			r.Y = (cy < oy ? cy : oy);
			r.Width = (cx - r.X > ox - r.X ? cx - r.X : ox - r.X);
			r.Height = (cy - r.Y > oy - r.Y ? cy - r.Y : oy - r.Y);
			//r.Right = (cx > ox ? cx : ox);
			//r.Bottom = (cy > oy ? cy : oy);
			return r;
		}

        public override void MouseMove(int mouseX, int mouseY, int tileX, int tileY)
        {
        	if (this.ToolManager.ActiveLayer == null)
        		return;
        
            // is the mouse held-down?
            if (down)
            {
				Rectangle r = getTileRect(downPosition.X, downPosition.Y, tileX, tileY, !isSelecting);
				
				if (!isSelecting)
				{
	                // if we're not erasing, we must be painting!
	                // possibly update preview, and remember mouse position.
	
	                if ((currentPosition.X != tileX) || (currentPosition.Y != tileY)) // i.e. have we moved the mouse?
	                    doPaintTileOp(r, true); // update temp preview! :)
				}
				else
				{
	                // Must be erasing.
	                hoverSprite.Visible = true;
	                hoverSprite.Depth = 1000; // todo: something.
	                hoverSprite.Transform.X = r.X * this.Map.GridSize;
	                hoverSprite.Transform.Y = r.Y * this.Map.GridSize;
	                hoverSprite.Transform.ScaleX = (r.Width + 1) * this.Map.GridSize;
	                hoverSprite.Transform.ScaleY = (r.Height + 1) * this.Map.GridSize;
            	}
				
				currentPosition = new Point(tileX, tileY);
			}
            else // mouse button is up -- so they're merely hovering?
            {
                if (SpritesheetSelection.Width > 0) // i.e. we have a selection
                {
                    downPosition = new Point(tileX, tileY);
					if (downPosition != currentPosition)
                    	doPaintTileOp(new Rectangle(downPosition, Size.Empty), true);
					currentPosition = downPosition;
                    downPosition = new Point(int.MinValue, int.MinValue);
                }
                else
				{
                    this.ToolManager.ActiveLayer.ClearTemporaryTiles();
					currentPosition = new Point(int.MinValue, int.MinValue);
				}
            }
        }

        private void doPaintTileOp(Rectangle rTile, bool tmp)
        {
      		if (this.ToolManager.ActiveLayer == null)
        		return;

            this.ToolManager.ActiveLayer.ClearTemporaryTiles();

            int rdx, rdy, rux, ruy;

            //rdx = (downPosition.X <= tileX ? downPosition.X : tileX); // down x
            //rdy = (downPosition.Y <= tileY ? downPosition.Y : tileY);
            ///rux = (downPosition.X <= tileX ? tileX : downPosition.X); // up x 
            // = (downPosition.Y <= tileY ? tileY : downPosition.Y);
			
			rdx = rTile.Left;
			rdy = rTile.Top;
			rux = rTile.Right;
			ruy = rTile.Bottom;

            //rux++; // just
            //ruy++; // because (round to next tile basically)

            if (SpritesheetSelection.Width == 0) // i.e. it has nothing selected
                return;

			int paintAreaHeight = (ruy - rdy) + (SpritesheetSelection.Height - ((ruy - rdy) % SpritesheetSelection.Height));
			int paintAreaWidth = (rux - rdx) + (SpritesheetSelection.Width - ((rux - rdx) % SpritesheetSelection.Width));
			
			// Do the Paint and get the return ICommand
			ICommand cmd = PaintTilesCommand.PaintTiles(this.ToolManager.ActiveLayer, this.CurrentSpritesheet, this.SpritesheetSelection, new Rectangle(rdx, rdy, paintAreaWidth, paintAreaHeight), tmp);
            
			Log.i(rTile.Left.ToString() + " " + rTile.Top.ToString() + " " + paintAreaWidth.ToString() + " " + paintAreaHeight.ToString());
			
            // If it was a temporary paint, throw away the ICommand
            // otherwise, add to the CommandManager!
            if (!tmp)
            	this.MapDocument.CommandManager.Do(cmd);

            //Log.i("PaintTileOp took " + (DateTime.Now - dtStart).TotalMilliseconds.ToString()
            //    + " ms.");
            //Log.i("(" + (DateTime.Now - dtPaintStart).TotalMilliseconds.ToString()
            //    + "ms of that was spent painttile-ing.)");
        }

    }
}

